#include "WorldHandler.h"
#include "DBCHandler.h"
#include "EventHandler.h"
#include "ScreenHandler.h"
#include "NetworkHandler.h"
#include "Random.h"
#include "Config.h"
#include <stdint.h>
#include <cstdlib>
#include <assert.h>
#include <algorithm>


#ifdef _WIN32
#include <windows.h>
	#define TEXTURE_PATH "..\\Gfx\\Textures\\"
	#define MODEL_PATH "..\\Gfx\\Models\\"
    #define PLAYER1_TEXTURE "..\\Gfx\\Textures\\black_angel.pcx"
	#define PLAYER_MESH "..\\Gfx\\Models\\faerie.md2"
	#define TEXTURE_FIRELANDS "..\\Gfx\\Textures\\firelands.jpg"

	#define TERRAIN_HEIGHTMAP "..\\Gfx\\Textures\\terrain-heightmap.bmp"
	#define TEXTURE_COMMONGROUND "..\\Gfx\\Textures\\commonground.jpg"
	#define TERRAIN_DETAILMAP "..\\Gfx\\Textures\\detailmap3.jpg"
	#define KNIGHT_MESH "..\\Gfx\\Models\\wagon.md2"
	#define COMPASS_MESH "..\\Gfx\\Models\\compass.md2"

    #define KNIGHT_TEXTURE "..\\Gfx\\Textures\\wagon.bmp"
	#define COMPASS_TEXTURE "..\\Gfx\\Textures\\compass.bmp"
	#define FIREBALL_TEXTURE "..\\Gfx\\Textures\\fireball.bmp"
	#define FROSTBALL_TEXTURE "..\\Gfx\\Textures\\frostball.jpg"
#else
	#define MODEL_PATH "../Gfx/Models/"
	#define TEXTURE_PATH "../Gfx/Textures/"
	#define TERRAIN_HEIGHTMAP "../Gfx/Textures/terrain-heightmap.bmp"
	#define TEXTURE_COMMONGROUND "../Gfx/Textures/commonground.jpg"
	#define TERRAIN_DETAILMAP "../Gfx/Textures/detailmap3.jpg"
    #define KNIGHT_MESH "../Gfx/Models/knight.md2"
    #define COMPASS_MESH "../Gfx/Models/compass.md2"
    #define COMPASSACTIVE_TEXTURE "../Gfx/Textures/compass_active.jpg"
	#define COMPASSINACTIVE_TEXTURE "../Gfx/Textures/compass_inactive.jpg"

    #define KNIGHT_TEXTURE "../Gfx/Textures/knight.jpg"
    #define PLAYER1_TEXTURE "../Gfx/Textures/black_angel.pcx"
    #define PLAYER_MESH "../Gfx/Models/faerie.md2"
	#define TEXTURE_FIRELANDS "../Gfx/Textures/firelands.jpg"
	#define FIREBALL_TEXTURE "../Gfx/Textures/fireball.bmp"
	#define FROSTBALL_TEXTURE "../Gfx/Textures/frostball.jpg"
#endif

#define KNIGHTSPAWN core::vector3df(700.f,0,300.f)
#define COMPASSSPAWN core::vector3df(600.f,0,300.f)
#define PLAYERSPAWN core::vector3df(500.f, 25,500.f)

#define RANDOMZONE_LOCATION 3000.f

WorldHandler globalWorldHandler;

WorldHandler::WorldHandler()
{
	playerCount = 0;
	//highest-1 = 0;
	randomTerrain = NULL;
	activeRandomZoneObject = NULL;
	randomSeed = NO_SEED;
	highestID = 0;
	isCompassObjectActive = true;
	aliveEnemiesCount = 0;
}

WorldHandler::~WorldHandler()
{

}

void WorldHandler::setFrameDelta(f32 passedDelta)
{
	frameDelta = passedDelta;
}

f32 WorldHandler::getFrameDelta()
{
	return frameDelta;
}

void WorldHandler::addWorldObject(WorldObject* object, int ID)
{
	unsigned int objectType = object->getObjectType();

	if(ID == -1)
	{
		ID = globalConfig.getIsServer()? highestID++ : highestID--;
	}
	object->setID(ID);

	if(WORLD_OBJECT == objectType)
	{
		worldObjects.push_back(object);
	}
	else if(PLAYER_OBJECT == objectType)
	{
		PlayerObject* playerObject = reinterpret_cast<PlayerObject*>(object);	
		
		playerObjects.push_back(playerObject);
		livingObjects.push_back(playerObject);
		movingObjects.push_back(playerObject);
		worldObjects.push_back(playerObject);
	}
	else if(ENEMY_OBJECT == objectType)
	{
		EnemyObject* enemyObject = reinterpret_cast<EnemyObject*>(object);
		
		enemyObjects.push_back(enemyObject);
		livingObjects.push_back(enemyObject);
		movingObjects.push_back(enemyObject);
		worldObjects.push_back(enemyObject);
		movingIDMap.insert(std::make_pair<int, MovingObject*>(ID, enemyObject));
		
		if(enemyObject->getIsInTown() == false)
		{
			enemyIDMap.insert(std::make_pair<int, EnemyObject*>(ID, enemyObject));
		}
	}
	else if(LIVING_OBJECT  == objectType)
	{
		LivingObject* livingObject = reinterpret_cast<LivingObject*>(object);	
		
		livingObjects.push_back(livingObject);
		movingObjects.push_back(livingObject);
		worldObjects.push_back(livingObject);
		movingIDMap.insert(std::make_pair<int, MovingObject*>(ID, livingObject));

		if(livingObject->getIsInTown() == false)
		{
			livingIDMap.insert(std::make_pair<int, LivingObject*>(ID, livingObject));
		}
	}
	else if(MOVING_OBJECT == objectType)
	{
		MovingObject* movingObject = reinterpret_cast<MovingObject*>(object);	
		
		movingObjects.push_back(movingObject);
		worldObjects.push_back(movingObject);
		worldObjects.push_back(movingObject);
		
		movingIDMap.insert(std::make_pair<int, MovingObject*>(ID, movingObject));
	}
	else if(ATTACK_OBJECT == objectType)
	{
		AttackObject* projectileObject = reinterpret_cast<AttackObject*>(object);
		
		// attackObjects.push_back(projectileObject);
		movingObjects.push_back(projectileObject);
		worldObjects.push_back(projectileObject);
		attackObjects.push_back(projectileObject);

		movingIDMap.insert(std::make_pair<int, MovingObject*>(ID, projectileObject));
		attackIDMap.insert(std::make_pair<int, AttackObject*>(ID, projectileObject));
	}

	if(NULL == object->getNode())
	{
		setupNode(object, ID);
	}

}

void WorldHandler::removeWorldObject(WorldObject* object)
{
	std::vector<WorldObject*>::iterator it1 = std::find(worldObjects.begin(), worldObjects.end(), object);
	if(it1 != worldObjects.end())
	{
		worldObjects.erase(it1);
	}
	
	std::vector<MovingObject*>::iterator it2 = std::find(movingObjects.begin(), movingObjects.end(), object);
	if(it2 != movingObjects.end())
	{
		movingObjects.erase(it2);
	}
	
	std::vector<LivingObject*>::iterator it3 = std::find(livingObjects.begin(), livingObjects.end(), object);
	if(it3 != livingObjects.end())
	{
		livingObjects.erase(it3);
	}
	
	std::vector<AttackObject*>::iterator it4 = std::find(attackObjects.begin(), attackObjects.end(), object);
	if(it4 != attackObjects.end())
	{
		attackObjects.erase(it4);
	}
	
	std::vector<EnemyObject*>::iterator it5 = std::find(enemyObjects.begin(), enemyObjects.end(), object);
	if(it5 != enemyObjects.end())
	{
		enemyObjects.erase(it5);
	}
	
	//TODO: check type first in order to decide whether to even attempt find
	
	if(attackIDMap.find(object->getID()) != attackIDMap.end())
	{
		attackIDMap.erase(attackIDMap.find(object->getID()));
	}
	
	if(movingIDMap.find(object->getID()) != movingIDMap.end())
	{
		movingIDMap.erase(movingIDMap.find(object->getID()));
	}
	
	if(livingIDMap.find(object->getID()) != livingIDMap.end())
	{
		livingIDMap.erase(livingIDMap.find(object->getID()));
	}	

	if(enemyIDMap.find(object->getID()) != enemyIDMap.end())
	{
		enemyIDMap.erase(enemyIDMap.find(object->getID()));
	}	
	
	if(object->getNode() != NULL)
	{	
		object->getNode()->remove();
	}
	
	free(object);
}

void WorldHandler::addZoneObject(ZoneObject* object)
{
	zoneObjects.push_back(object);
}

ZoneObject* WorldHandler::getZoneObject(unsigned int index)
{
	return zoneObjects.at(index);
}

ZoneObject* WorldHandler::getRandomRandomZoneObject()
{
	return zoneObjects.at(Random()%zoneObjects.size());
}

unsigned int WorldHandler::getWorldObjectsCount()
{
	return worldObjects.size();
}

unsigned int WorldHandler::getMovingObjectsCount() // FIXME: return size_t
{
	return movingObjects.size();
}

unsigned int WorldHandler::getPlayerObjectsCount()
{
	return playerObjects.size();
}

std::vector<WorldObject*> WorldHandler::getWorldObjects()
{
	return worldObjects;
}

std::vector<MovingObject*> WorldHandler::getMovingObjects()
{
	return movingObjects;
}

std::vector<LivingObject*> WorldHandler::getLivingObjects()
{
	return livingObjects;
}

std::vector<EnemyObject*> WorldHandler::getEnemyObjects()
{
	return enemyObjects;
}

std::vector<PlayerObject*> WorldHandler::getPlayerObjects()
{
	return playerObjects;
}

std::map<int, LivingObject*> WorldHandler::getLivingIDMap()
{
	return livingIDMap;
}

std::map<int, MovingObject*> WorldHandler::getMovingIDMap()
{
	return movingIDMap;
}

std::map<int, EnemyObject*> WorldHandler::getEnemyIDMap()
{
	return enemyIDMap;
}

std::map<int, AttackObject*> WorldHandler::getAttackIDMap()
{
	return attackIDMap;
}

std::vector<AttackObject*> WorldHandler::getAttackObjects()
{
	return attackObjects;
}

void WorldHandler::setPlayerCount(unsigned int passedPlayerCount)
{
	playerCount = passedPlayerCount;
}

unsigned int WorldHandler::getPlayerCount()
{
	return playerCount;
}

void WorldHandler::setLocalPlayerIndex(unsigned int passedLocalPlayerIndex)
{
	localPlayerIndex = passedLocalPlayerIndex;
}

unsigned int WorldHandler::getLocalPlayerIndex()
{
	return localPlayerIndex;
}


PlayerObject* WorldHandler::setupNewPlayer(const std::string name, core::vector3df position)
{	
	setPlayerCount(getPlayerCount()+1);
	
	PlayerObject *player = new PlayerObject();
	player->setIsConnected(true);
	player->setName(name);
	player->setIsDead(false);
	player->setIsMoving(false);
	player->setStartMoving(false);
	player->setSpeedConstant(125.f);
	player->setIsInTown(true);
	if(position == core::vector3df(0, 0, 0))
	{
		position = PLAYERSPAWN;
	}
	player->setPosition(position);
	player->setMovementInfo(position, core::vector3df(0, 0, 0), core::vector3df(1,1,1), 0.f, 0.f);
	player->setPlayerIndex(getPlayerCount()-1);
	
	addWorldObject(player);

	logMessage("Set up new player with name " + name + ".");

	return player;
}

AttackObject* WorldHandler::setupAttack(core::vector3df startPos, core::matrix4 passedMat, AttackType type, bool addToWorld, const std::string& originatingPlayer, int ID)
{
	AttackObject* attackObject = new AttackObject();
	attackObject->setAttackType(type);
	
	attackObject->setPosition(startPos);
	core::matrix4 mat = passedMat;
	//core::matrix4 mat = node->getRelativeTransformation();
	attackObject->setMat(mat);

	if(addToWorld == true)
	{
		//TODO: setStartMoving(true) instead and let moveObjects fix speed etc
		f32 speedConst = attackObject->getSpeedConstant();
		f32 frameDelta = globalWorldHandler.getFrameDelta();
		
		core::vector3df direction = core::vector3df(mat[0], mat[1], mat[2]);
		core::vector3df moveSpeed = direction;
		
		moveSpeed.Z *= speedConst * frameDelta;
		moveSpeed.X *= speedConst * frameDelta;
		
		attackObject->setMovementInfo(startPos + direction * 11, core::vector3df(1, 1, 1), moveSpeed, 0, attackObject->getRange());
		attackObject->setStartMoving(false);
		attackObject->setIsMoving(true);
		
		scene::ISceneNode* node = globalScreenHandler.getSmgr()->addEmptySceneNode();
		node->setPosition(startPos);
		attackObject->setNode(reinterpret_cast<scene::IMeshSceneNode*>(node));
		attackObject->setOriginatingPlayer(originatingPlayer);
		
		if(type != MELEE)
		{
			attackObject->setupParticleSystem();
		}
		addWorldObject(attackObject, ID);
	}
    
	return attackObject;
}

scene::IParticleSystemSceneNode* WorldHandler::createParticleSystem(AttackType type, scene::ISceneNode* parentNode)
{
	video::IVideoDriver* driver = globalScreenHandler.getDriver();
	scene::ISceneManager* smgr = globalScreenHandler.getSmgr();

	scene::IParticleSystemSceneNode* ps = smgr->addParticleSystemSceneNode(false, parentNode);
	scene::IParticleEmitter* em = NULL;
	scene::IParticleAffector* paf = NULL;
	
	switch(type)
	{
		case FIREBALL:
		{
			//scene::ISceneNode* light = smgr->addLightSceneNode(0, core::vector3df(0,1000,0),
            //    video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 800.0f);
             //light->setParent(ps);
             
			em = ps->createBoxEmitter(
				core::aabbox3d<f32>(-3,0,-3,3,1,3),
				core::vector3df(0.0f,0.03f,0.0f),
				80,100,
				video::SColor(0,255,255,255), video::SColor(0,255,255,255),
				400,1100);
                
			em->setMinStartSize(core::dimension2d<f32>(30.0f, 40.0f));
				em->setMaxStartSize(core::dimension2d<f32>(30.0f, 40.0f));
				paf =  ps->createFadeOutParticleAffector();
				ps->setMaterialTexture(0, driver->getTexture(FIREBALL_TEXTURE));
				
		}
		break;
		case FROSTBALL:
		{
			em = ps->createBoxEmitter(
				core::aabbox3d<f32>(-3,0,-3,3,1,3),
				core::vector3df(0.0f,0.03f,0.0f),
				80,100,
				video::SColor(0,255,255,255), video::SColor(0,255,255,255),
				400,1100);
                
			em->setMinStartSize(core::dimension2d<f32>(30.0f, 40.0f));
				em->setMaxStartSize(core::dimension2d<f32>(30.0f, 40.0f));
				paf =  ps->createFadeOutParticleAffector();
				reinterpret_cast<scene::IParticleFadeOutAffector*>(paf)->setFadeOutTime(2400.f);
				ps->setMaterialTexture(0, driver->getTexture(FROSTBALL_TEXTURE));
		}
		break;
		default:
			std::cout << "CASE " <<  type << "\n";
			assert("CASE NOT HANDLED in WorldHandler::createParticleSystem()" == 0);
	}
 	
 	if(NULL != em)
 	{
 		ps->setEmitter(em);
 		em->drop();
	}
	
	if(NULL != paf)
	{
		ps->addAffector(paf);
		paf->drop();
	}
	
 	ps->setMaterialFlag(video::EMF_LIGHTING, false);
 	ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
 	ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);
 	
 	return ps;
}

void WorldHandler::setupWorldLighting()
{
	scene::ISceneManager* smgr = globalScreenHandler.getSmgr();

	smgr->setShadowColor(video::SColor(150,0,0,0));
	smgr->setAmbientLight(video::SColorf(0.5f, 0.5f, 0.5f, 1.0f));

	smgr->addLightSceneNode(0, core::vector3df(500,500,500), video::SColorf(0.5f, 0.5f, 0.5f, 1.0f), 800.f);
	smgr->addLightSceneNode(0, core::vector3df(RANDOMZONE_LOCATION+500,500,RANDOMZONE_LOCATION+500), video::SColorf(0.5f, 0.5f, 0.5f, 1.0f), 800.f);
}

scene::ITerrainSceneNode* WorldHandler::setupTerrain(const char* texture, f32 location)
{
    video::IVideoDriver* driver = globalScreenHandler.getDriver();
    scene::ISceneManager* smgr = globalScreenHandler.getSmgr();

    scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
            TERRAIN_HEIGHTMAP,
            0,                                      // parent node
            TERRAINID,                              // node id
            core::vector3df(location, 0.f, location),         // position
            core::vector3df(0.f, 0.f, 0.f),         // rotation
            core::vector3df(5.f, 4.4f, 5.f),        // scale
            video::SColor ( 255, 255, 255, 255 ),   // vertexColor
            4,                                      // maxLOD
            scene::ETPS_17,                         // patchSize
            4                                       // smoothFactor
            );

    terrain->setMaterialFlag(video::EMF_LIGHTING, true);
    terrain->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, true);
    terrain->setMaterialFlag(video::EMF_TRILINEAR_FILTER, true);
    //terrain->setMaterialFlag(video::EMF_FOG_ENABLE, true);
    terrain->setMaterialTexture(0, driver->getTexture(texture)); // skulfack har
    terrain->setMaterialType(video::EMT_DETAIL_MAP);

    terrain->scaleTexture(3.0f, 0);

    //terrain->getMesh()->setMaterialFlag(video::EMF_LIGHTING, true);
	
    globalScreenHandler.swapTerrainTriangleSelectors();
    globalScreenHandler.setTerrainTriangleSelector(smgr->createTerrainTriangleSelector(terrain));

    return terrain;
}
#if 0
void WorldHandler::runThroughRemoveQueue()
{
    while(removeQueue.size() != 0)
    {
        WorldObject* worldObject = removeQueue.front();
        reinterpret_cast<scene::ISceneNode*>(worldObject->getNode())->remove();

        switch(worldObject->getObjectType())
        {
            case WORLD_OBJECT:
                free(worldObject);
                break;
            case MOVING_OBJECT:
                free(reinterpret_cast<MovingObject*>(worldObject));
                break;
            case LIVING_OBJECT:
                free(reinterpret_cast<LivingObject*>(worldObject));
                break;
        }

        removeQueue.pop();
    }
}
#endif

void WorldHandler::setupNode(WorldObject* worldObject, int nodeID)
{    
	video::IVideoDriver* driver = globalScreenHandler.getDriver();
	scene::ISceneManager* smgr = globalScreenHandler.getSmgr();
	
	scene::ISceneNode* node = worldObject->getNode();

    if(worldObject->getObjectType() == PLAYER_OBJECT)
    {
        node = smgr->addAnimatedMeshSceneNode(((scene::IAnimatedMesh*)smgr->getMesh(PLAYER_MESH)->getMesh(1,1)), 0, -1, worldObject->getPosition());
        node->setMaterialTexture(0, driver->getTexture(PLAYER1_TEXTURE));
        reinterpret_cast<PlayerObject*>(worldObject)->setStartMoving(false);
    }
    #if 0
    else if(worldObject->getObjectType() == LIVING_OBJECT)
	{
		scene::ITriangleSelector*  selector = smgr->createTriangleSelector(node);
		node->setTriangleSelector(selector);
		selector->drop();
	}
	#endif
    else if(worldObject->getObjectType() != ATTACK_OBJECT)
	{        
		std::string modelPath = MODEL_PATH + worldObject->getModelName();
		node = smgr->addAnimatedMeshSceneNode(((scene::IAnimatedMesh*)smgr->getMesh(modelPath.c_str())->getMesh(1,1)), 0, -1, worldObject->getPosition());
		std::string texturePath = TEXTURE_PATH + worldObject->getTextureName();
		node->setMaterialTexture(0, driver->getTexture(texturePath.c_str()));
	}

	if(worldObject->getObjectType() != ATTACK_OBJECT)
	{
//	    reinterpret_cast<scene::IAnimatedMeshSceneNode*>(node)->setMD2Animation(scene::EMAT_STAND);
	    reinterpret_cast<scene::IAnimatedMeshSceneNode*>(node)->setCurrentFrame(rand()%(reinterpret_cast<scene::IAnimatedMeshSceneNode*>(node)->getEndFrame()));
	    reinterpret_cast<scene::IAnimatedMeshSceneNode*>(node)->setMaterialFlag(video::EMF_LIGHTING, true);
	//    node->setMaterialFlag(video::EMF_FOG_ENABLE, true);
	    //reinterpret_cast<scene::IAnimatedMeshSceneNode*>(node)->addShadowVolumeSceneNode();

	    worldObject->setNode(reinterpret_cast<scene::IMeshSceneNode*>(node));
	    reinterpret_cast<MovingObject*>(worldObject)->setAnimation(scene::EMAT_STAND, true);
		node->setID(nodeID);
	}

}

void WorldHandler::moveObjects()
{		
	for(size_t i = 0; i < movingObjects.size(); ++i)
	{
		MovingObject* movingObject = movingObjects[i];

		if(movingObject->getObjectType() == PLAYER_OBJECT
		&& reinterpret_cast<PlayerObject*>(movingObject)->getIsConnected() == false)
		{
			continue;
		}
		else if(movingObject->getObjectType() == ENEMY_OBJECT
		&& getAreAllPlayersDead() == true)
		{
			EnemyObject* enemyObject = reinterpret_cast<EnemyObject*>(movingObject);
			if(enemyObject->getIsDead() == false
			&& enemyObject->getAnimation() != scene::EMAT_SALUTE)
			{
				enemyObject->setStartAttacking(false);
				enemyObject->setIsAttacking(false);
				enemyObject->setAnimation(scene::EMAT_SALUTE, true);
				reinterpret_cast<scene::IAnimatedMeshSceneNode*>(enemyObject->getNode())->setCurrentFrame(Random()%reinterpret_cast<scene::IAnimatedMeshSceneNode*>(enemyObject->getNode())->getEndFrame());
				enemyObject->setIsMoving(false);
			}
			continue;
		}
		
		
		updateAnimation(movingObject);
			
		//	movingObject->setPosition(movingObject->getPosition());

		MovementInfo* newMovementInfo = movingObject->getMovementInfo();
		core::vector3df oldPosition = movingObject->getPosition();
		core::vector3df newPosition = newMovementInfo->startPos + newMovementInfo->moveSpeed * newMovementInfo->timesMoved;		
		//#if 0
		/*
		if(newMovementInfo->startPos != core::vector3df(0, 0, 0)
		&& movingObject->getObjectType() != ATTACK_OBJECT)
		{
		    core::vector3df distance = newPosition - oldPosition;
		    if(sqrt(distance.X*distance.X) > 20
			    || sqrt(distance.Z*distance.Z) > 20)
		    {
			movingObject->setPosition(newPosition);
			movingObject->stopMoving();
		    }		
		}*/
		//#endif

		if(movingObject->getObjectType() == PLAYER_OBJECT
		&& oldPosition == core::vector3df(0, 0, 0))
		{
		    movingObject->setPosition(newPosition);
		}

		if(movingObject->getStartMoving() == true)
		{
			scene::IMeshSceneNode* node = movingObject->getNode();

			if(newMovementInfo->destinationPos == core::vector3df(0,0,0))
			{
				newMovementInfo->destinationPos = newMovementInfo->startPos;
			}

			newMovementInfo->startPos = movingObject->getPosition();
			core::vector3df moveSpeed = ((core::vector3df)(newMovementInfo->destinationPos - newMovementInfo->startPos)).normalize();

			f32 speedConst = movingObject->getSpeedConstant();
			f32 frameDelta = globalWorldHandler.getFrameDelta();
			moveSpeed.Z *= speedConst * frameDelta;
			moveSpeed.X *= speedConst * frameDelta;

			f32 timesToMove = (newMovementInfo->destinationPos.X - newMovementInfo->startPos.X) / moveSpeed.X;

			if(movingObject->getObjectType() == PLAYER_OBJECT
			&& *reinterpret_cast<PlayerObject*>(movingObject)->getPlayerIndex() == localPlayerIndex)
			{
				timesToMove -= timesToMove / 10; //mouse click inaccuracycompensation
			}
			
			movingObject->setMovementInfo(newMovementInfo->startPos, newMovementInfo->destinationPos, moveSpeed, 0, timesToMove);

			int minTimesToMove = (movingObject->getObjectType() == PLAYER_OBJECT) ? 5 : 20;
			
			if(movingObject->getObjectType() == ENEMY_OBJECT
			&& movingObject->getIsMoving() == false)
			{
				minTimesToMove += 10;
			}
				
			if(timesToMove > minTimesToMove)
			{
				if(node != NULL
				&& movingObject->getObjectType() != ATTACK_OBJECT
				&& movingObject->getAnimation() != scene::EMAT_RUN)
				{
					movingObject->setAnimation(scene::EMAT_RUN, true);
					reinterpret_cast<scene::IAnimatedMeshSceneNode*>(movingObject->getNode())->setCurrentFrame(rand()%reinterpret_cast<scene::IAnimatedMeshSceneNode*>(movingObject->getNode())->getEndFrame());			
				}

				movingObject->setIsMoving(true);		        
			}
			else if(movingObject->getObjectType() == ENEMY_OBJECT
			&& movingObject->getAnimation() != scene::EMAT_STAND)
			{
				movingObject->setAnimation(scene::EMAT_STAND, true);
				reinterpret_cast<scene::IAnimatedMeshSceneNode*>(movingObject->getNode())->setCurrentFrame(rand()%reinterpret_cast<scene::IAnimatedMeshSceneNode*>(movingObject->getNode())->getEndFrame());			
				movingObject->setIsMoving(false);
			}
			movingObject->setStartMoving(false);
		}

		if(movingObject->getIsMoving() == true)
		{		
			MovementInfo* objectMovementInfo = movingObject->getMovementInfo();
			objectMovementInfo->timesMoved++;
			core::vector3df newPosition(objectMovementInfo->startPos + objectMovementInfo->moveSpeed * objectMovementInfo->timesMoved);
			core::vector3df oldPosition = movingObject->getPosition();
			movingObject->setPosition(newPosition);

			core::aabbox3df boxxy;
			/*if(movingObject->getObjectType() == ATTACK_OBJECT)
			{	
				boxxy = movingObject->getNode()->getTransformedBoundingBox();
			}
			else*/
		//			{
				boxxy = core::aabbox3df(newPosition.X, newPosition.Y, newPosition.Z, newPosition.X, newPosition.Y, newPosition.Z);
		//		}
			
			bool validCollision = false;
			bool movingObjectRemoved = false;
			for(size_t j = 0; j < worldObjects.size(); ++j)
			{
				if(boxxy.intersectsWithBox(worldObjects.at(j)->getBoundingBox()))
				{
					if(true == (validCollision = globalEventHandler.handleCollisionEvent(
					static_cast<WorldObject*>(movingObject), worldObjects.at(j), &movingObjectRemoved)))
					{
						break;
					}
				}
			}

			//also check for collision with compassObject which is not checked in above loop
			if(validCollision == false
			&& boxxy.intersectsWithBox(compassObject->getNode()->getTransformedBoundingBox()))
			{
				validCollision = globalEventHandler.handleCollisionEvent(static_cast<WorldObject*>(movingObject), compassObject, &movingObjectRemoved);
			}
			if(movingObjectRemoved == true)
			{
				continue;
			}
			else if(validCollision == true)
			{
				movingObject->setPosition(oldPosition);
			    objectMovementInfo->timesMoved--;
			}

			scene::IMeshSceneNode* node = movingObject->getNode();			
			if(node != NULL
			&& movingObject->getObjectType() != ATTACK_OBJECT)
			{
				movingObject->faceTarget(objectMovementInfo->startPos + objectMovementInfo->moveSpeed * (objectMovementInfo->timesMoved+1));
			}
		    //外人Pig
			if(objectMovementInfo->timesMoved >= objectMovementInfo->timesToMove)
			{
				objectMovementInfo->startPos = objectMovementInfo->destinationPos;
				objectMovementInfo->destinationPos = core::vector3df(0,0,0);
				movingObject->setIsMoving(false);
				if(node != NULL)
				{
					if(movingObject->getObjectType() != ATTACK_OBJECT)
					{
						movingObject->setAnimation(scene::EMAT_STAND, true);
					}
					else
					{
						removeWorldObject(static_cast<WorldObject*>(movingObject));
					}
				}
			}
		}
	}
}

void WorldHandler::updateAnimation(MovingObject* movingObject)
{
	if(movingObject->getObjectType() != ATTACK_OBJECT)
	{
		scene::IAnimatedMeshSceneNode* animNode = reinterpret_cast<scene::IAnimatedMeshSceneNode*>(movingObject->getNode());
		
		if(movingObject->getObjectType() == ENEMY_OBJECT
		|| movingObject->getObjectType() == PLAYER_OBJECT
		|| movingObject->getObjectType() == LIVING_OBJECT)
		{
			if(reinterpret_cast<LivingObject*>(movingObject)->getIsDead() == true 
			&& movingObject->getAnimation() != scene::EMAT_DEATH_FALLBACKSLOW
			&& movingObject->getAnimation() != scene::EMAT_DEATH_FALLFORWARD
			&& movingObject->getAnimation() != scene::EMAT_DEATH_FALLBACK)
			{
				int anim = rand()%3;
				irr::scene::EMD2_ANIMATION_TYPE animationType;
				switch(anim)
				{
					case 0:
						animationType = scene::EMAT_DEATH_FALLBACKSLOW;
						break;
					case 1:
						animationType = scene::EMAT_DEATH_FALLFORWARD;
						break;
					default:
						animationType = scene::EMAT_DEATH_FALLBACK;
				}
				
				movingObject->setAnimation(animationType, false);
				movingObject->setStartMoving(false);
				movingObject->setIsMoving(false);
			}
		}
		else if(animNode->getFrameNr() == animNode->getEndFrame())
		{
			switch((movingObject->getAnimation()))
			{
				case scene::EMAT_ATTACK:
				case scene::EMAT_PAIN_A:
				case scene::EMAT_PAIN_B:
					if(movingObject->getIsMoving() == true)
					{
						movingObject->setAnimation(scene::EMAT_RUN, true);
					}
					else
					{
						movingObject->setAnimation(scene::EMAT_STAND, true);
					}
				break;
			}
		}
	}
}

void WorldHandler::setupTown()
{	
	video::IVideoDriver* driver = globalScreenHandler.getDriver();
	scene::ISceneManager* smgr = globalScreenHandler.getSmgr();

	setupTerrain(TEXTURE_COMMONGROUND, 0.f);

#if 0
	WorldObject *knightNPC = new WorldObject;
	scene::IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(((scene::IAnimatedMesh*)smgr->getMesh(KNIGHT_MESH)->getMesh(1,1)), 0, -1, KNIGHTSPAWN);

	node->setMaterialTexture(0, driver->getTexture(KNIGHT_TEXTURE));
	node->setMD2Animation(scene::EMAT_STAND);

	node->setMaterialFlag(video::EMF_LIGHTING, true);
	node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);

	knightNPC->setNode(reinterpret_cast<irr::scene::IMeshSceneNode*>(node));
	knightNPC->faceTarget(EAST);

	knightNPC->setIsInTown(true);

	addWorldObject(knightNPC);
#endif 

	compassObject = new WorldObject;
	scene::IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(((scene::IAnimatedMesh*)smgr->getMesh(COMPASS_MESH)->getMesh(1,1)), 0, -1, COMPASSSPAWN);

	node->setScale(core::vector3df(2.5,2.5,2.5));
	node->setMaterialTexture(0, driver->getTexture(COMPASSACTIVE_TEXTURE));
	//    node->setMD2Animation(scene::EMAT_STAND);
	node->setMaterialFlag(video::EMF_LIGHTING, true);
	node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
	//node->setMaterialFlag(video::EMF_FOG_ENABLE, true);

	node->setID(COMPASSID);
	//    node->addShadowVolumeSceneNode();

	compassObject->setNode(reinterpret_cast<irr::scene::IMeshSceneNode*>(node));
	//reinterpret_cast<>(compassObject)->setAnimation(scene::EMAT_STAND, true);
	compassObject->faceTarget(EAST);

	compassObject->setPosition(COMPASSSPAWN);
	compassObject->setIsInTown(true);



	addWorldObject(compassObject);
}


void WorldHandler::setupRandomZone()
{
	compassObject->setPosition(core::vector3df(RANDOMZONE_LOCATION+30, 0.f, RANDOMZONE_LOCATION+30));
	setIsCompassObjectActive(false);
	
	SeedRandom(randomSeed);
	activeRandomZoneObject = getRandomRandomZoneObject();
	std::string texturePath = TEXTURE_PATH + activeRandomZoneObject->getTextureName();
	if(randomTerrain == NULL)
	{
		randomTerrain = setupTerrain(texturePath.c_str(), RANDOMZONE_LOCATION); // hack 200 00switchbakk
	}
	else
	{
		video::IVideoDriver* driver = globalScreenHandler.getDriver();
		randomTerrain->setMaterialTexture(0, driver->getTexture(texturePath.c_str())); // skulfack har
		globalScreenHandler.swapTerrainTriangleSelectors();
	}

	
	
	for(size_t i = 0; i < playerObjects.size(); ++i)
	{
		PlayerObject* playerObject = playerObjects[i];

		playerObject->setStartMoving(false);
		playerObject->setIsMoving(false);
		playerObject->setIsInTown(false);
		playerObject->setPosition(core::vector3df(RANDOMZONE_LOCATION+150, 25, RANDOMZONE_LOCATION+150));
		playerObject->setMovementInfo(core::vector3df(RANDOMZONE_LOCATION+150, 25, RANDOMZONE_LOCATION+150), core::vector3df(0, 0, 0), core::vector3df(0, 0, 0), 0, 0);
	//		reinterpret_cast<scene::IAnimatedMeshSceneNode*>(playerObject->getNode())->setMD2Animation(scene::EMAT_STAND);
		playerObject->setAnimation(scene::EMAT_STAND, true);
	}

	//TODO: static world Objects
	/* */
	
	//Enemies
	
	if(globalConfig.getIsServer() == true)
	{
		aliveEnemiesCount = 5 + additionalEnemyCount;
		
		for(int i = 0; i < aliveEnemiesCount; ++i)
		{	
			LivingObject* enemy = globalDBCHandler.createEnemy(activeRandomZoneObject->getRandomEnemyKey(), core::vector3df(RANDOMZONE_LOCATION+ 300 + Random()%200, 25, RANDOMZONE_LOCATION+300+Random()%200));
			
			reinterpret_cast<scene::IAnimatedMeshSceneNode*>(enemy->getNode())->setCurrentFrame(Random()%reinterpret_cast<scene::IAnimatedMeshSceneNode*>(enemy->getNode())->getEndFrame());

			enemy->faceTarget(core::vector3df(RANDOMZONE_LOCATION+Random()%1000, 0, RANDOMZONE_LOCATION+Random()%1000));
		}
		
		additionalEnemyCount += 2+rand()%3;
	}
}

void WorldHandler::dismantleRandomZone()
{
    unsigned int worldIt = 0;
    unsigned int movingIt = 0;
    unsigned int livingIt = 0;
//    unsigned int projectileIt = 0;

    //FIXME: ta bort bankkod som har 0 maintainability om nagon orkar
    while(worldIt < worldObjects.size())
    {
    #if 0
        if(movingIt < movingObjects.size())
        {
            if(movingObjects.at(movingIt)->getObjectType() == PLAYER_OBJECT
            || movingObjects.at(movingIt)->getObjectType() == ATTACK_OBJECT
            || movingObjects.at(movingIt)->getIsInTown() == true)
            {
                movingIt++;
            }
            else
            {
                std::vector<MovingObject*>::iterator it = movingObjects.begin() + movingIt;
                movingObjects.erase(it);
            }
        }

        if(livingIt < livingObjects.size())
        {
            if(livingObjects.at(livingIt)->getObjectType() == PLAYER_OBJECT
            || livingObjects.at(livingIt)->getIsInTown() == true)
            {
                livingIt++;
            }
            else
            {
                std::vector<LivingObject*>::iterator it = livingObjects.begin() + livingIt;
                livingObjects.erase(it);
            }
        }
#if 0
        if(projectileIt < attackObjects.size())
        {
            std::vector<AttackObject*>::iterator it = attackObjects.begin() + projectileIt;
            attackObjects.erase(it);
        }
#endif
        if(worldObjects.at(worldIt)->getObjectType() == PLAYER_OBJECT)
        {
            reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setPosition(PLAYERSPAWN);
            reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setMovementInfo(PLAYERSPAWN, PLAYERSPAWN, core::vector3df(0,0,0), 0.f, 0.f);
            reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setIsMoving(false);
            reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setStartMoving(false);
//			reinterpret_cast<scene::IAnimatedMeshSceneNode*>(worldObjects.at(worldIt)->getNode())->setMD2Animation(scene::EMAT_STAND);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setAnimation(scene::EMAT_STAND, true);
            
            worldIt++;
        }
        else if(worldObjects.at(worldIt)->getIsInTown() == true
        || worldObjects.at(worldIt)->getObjectType() == ATTACK_OBJECT)
        {
            worldIt++;
        }
        else
        {
			WorldObject* worldObject = worldObjects.at(worldIt);

			scene::ISceneNode* node = reinterpret_cast<scene::ISceneNode*>(worldObject->getNode());
			if(node != NULL
			&& worldObject->getObjectType() != ATTACK_OBJECT) //TODO: extra regard for particles?
			{
		        scene::ITriangleSelector* selector = node->getTriangleSelector();
				if(selector != NULL)
				{
				    globalScreenHandler.getMetaSelector()->removeTriangleSelector(selector);
				}
				node->remove();
		    }
			
            std::vector<WorldObject*>::iterator it = worldObjects.begin() + worldIt;


            worldObjects.erase(it);
            free(worldObject);
        }
    #endif
		if(worldObjects.at(worldIt)->getObjectType() == PLAYER_OBJECT)
		{
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setPosition(PLAYERSPAWN);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setMovementInfo(PLAYERSPAWN, PLAYERSPAWN, core::vector3df(0,0,0), 0.f, 0.f);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setIsMoving(false);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setStartMoving(false);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setIsInTown(true);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setIsDead(false);
			
	//			reinterpret_cast<scene::IAnimatedMeshSceneNode*>(worldObjects.at(worldIt)->getNode())->setMD2Animation(scene::EMAT_STAND);
			reinterpret_cast<PlayerObject*>(worldObjects.at(worldIt))->setAnimation(scene::EMAT_STAND, true);
		}
		else if(worldObjects.at(worldIt)->getIsInTown() == false)
		{
			removeWorldObject(worldObjects.at(worldIt--));
		}
		worldIt++;
	}
    
	livingIDMap.clear();
	movingIDMap.clear();
	attackIDMap.clear();

	globalScreenHandler.swapTerrainTriangleSelectors();
	activeRandomZoneObject = NULL;
	compassObject->setPosition(COMPASSSPAWN);
	setIsCompassObjectActive(true);
}

void WorldHandler::setRandomSeed(unsigned int seed)
{
    randomSeed = seed;
}

unsigned int WorldHandler::getRandomSeed()
{
    return randomSeed;
}

ZoneObject* WorldHandler::getActiveRandomZoneObject()
{
    return activeRandomZoneObject;
}

WorldObject* WorldHandler::getCompassObject()
{
	return compassObject;
}

void WorldHandler::setHighestID(const int passedHighestID)
{
	highestID = passedHighestID;
}

void WorldHandler::runAI()
{	
	for(size_t i = 0; i < enemyObjects.size(); ++i)
	{
		EnemyObject* enemyObject = enemyObjects[i];
	
		if(enemyObject->getIsDead() == true)
		{
			continue;
		}
		
		/* taunt if everyone is dead*/
		//TODO: don't have this both here and in moveObjects
		if(getAreAllPlayersDead() == true)
		{
			if(enemyObject->getAnimation() != scene::EMAT_SALUTE)
			{
				enemyObject->setMovementInfo(core::vector3df(0, 0, 0), core::vector3df(0, 0, 0), core::vector3df(0, 0, 0), 0, 0);
				enemyObject->setStartAttacking(false);
				enemyObject->setIsAttacking(false);
				enemyObject->setAnimation(scene::EMAT_SALUTE, true);
				reinterpret_cast<scene::IAnimatedMeshSceneNode*>(enemyObject->getNode())->setCurrentFrame(Random()%reinterpret_cast<scene::IAnimatedMeshSceneNode*>(enemyObject->getNode())->getEndFrame());
				enemyObject->setIsMoving(false);
			}
			continue;
		}
		
		/* attack */
		if(enemyObject->getTarget() != NULL)
		{
			core::vector3df targetDistance = enemyObject->getPosition() - enemyObject->getTarget()->getPosition();

			/* reset attack if cooldown has passed */
			f32 now = globalScreenHandler.getDevice()->getTimer()->getTime();
			
			if(enemyObject->getIsAttacking() == true
			&& ((now - enemyObject->getTimeOfLastAttack()) > enemyObject->getAttackTime()))
			{
				std::string name = "enemy";
				AttackObject* attackObject = globalWorldHandler.setupAttack(enemyObject->getPosition(), enemyObject->getNode()->getRelativeTransformation(), MELEE, true, name);				
				enemyObject->setIsAttacking(false);
			}
			
			if(enemyObject->getIsAttacking() == false)
			{
				if(sqrt(targetDistance.X*targetDistance.X) < 30
				&& sqrt(targetDistance.Z*targetDistance.Z) < 30)
				{
					enemyObject->setStartAttacking(false);
					enemyObject->setIsAttacking(true);
					enemyObject->setAnimation(scene::EMAT_ATTACK, false);
					enemyObject->setTimeOfLastAttack(now);
					enemyObject->setIsMoving(false);
				}
				#if 0
				else
				{
					enemyObject->setIsAttacking(false);
				}
				#endif
				//globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(projectileObject), ATTACKOBJECT);
			}
		}
		
		/* acquire target */
		if(enemyObject->getTarget() == NULL
		|| enemyObject->getTarget()->getIsDead() == true)
		{
			PlayerObject* newTarget;
			
			do 
			{
				unsigned int randPlayer = rand() % playerObjects.size();
				newTarget = playerObjects[randPlayer];
			} while (newTarget->getIsDead() == true);
			
			enemyObject->setTarget(newTarget);
		}
		
		/* move */
		if(enemyObject->getIsAttacking() == false)
		{
			PlayerObject* playerObject  = enemyObject->getTarget();
			MovementInfo* movementInfo = playerObject->getMovementInfo();
			core::vector3df startPos(enemyObject->getPosition());
			core::vector3df destPos = core::vector3df(destPos = playerObject->getPosition());
			
			enemyObject->setMovementInfo(startPos, destPos, core::vector3df(0, 0, 0), 0, 0);
			enemyObject->setStartMoving(true);
		}
	}	
}

unsigned int WorldHandler::getAliveEnemiesCount()
{
	return aliveEnemiesCount;
}

void WorldHandler::decreaseAliveEnemiesCount()
{
	--aliveEnemiesCount;
}

void WorldHandler::setIsCompassObjectActive(bool passedIsActive)
{
	isCompassObjectActive = passedIsActive;
	if(passedIsActive == true)
	{
		compassObject->getNode()->setMaterialTexture(0, globalScreenHandler.getDriver()->getTexture(COMPASSACTIVE_TEXTURE));
		if(globalConfig.getIsServer() == true)
		{
			std::string empty = "";
			globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(&empty), ACTIVATECOMPASS);
		}
	}
	else
	{
		compassObject->getNode()->setMaterialTexture(0, globalScreenHandler.getDriver()->getTexture(COMPASSINACTIVE_TEXTURE));
		
		if(globalConfig.getIsServer() == true)
		{
			std::string empty = "";
			globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(&empty), INACTIVATECOMPASS);
		}
	}
}

bool WorldHandler::getIsCompassObjectActive()
{
	return isCompassObjectActive;
}

bool WorldHandler::getAreAllPlayersDead()
{			
	bool allDead = true;
	
	for(size_t i = 0; i < playerObjects.size(); ++i)
	{
		PlayerObject* playerObject = playerObjects[i];
		
		if(playerObject->getIsDead() == false
		|| playerObject->getIsConnected() == false)
		{
			allDead = false;
			break;
		}
	}

	return allDead;
}

void WorldHandler::setAdditionalEnemyCount(unsigned int passedCount)
{
	additionalEnemyCount = passedCount;
}